var LightSample = function(l, el) {
	this.l = l;
	this.el = el;
};

LightSample.zero = new LightSample(Vector3.zero, Color.black);

LightSample.prototype.initialize = function() {};

LightSample.prototype.shade = function(scene, position) {
	var ray = new Ray3(position, this.l.subtract(position).normalize());
	var result = scene.intersect(ray);
	if(result.geometry) {
		return 0;
	}
	return 1;
};

LightSample.prototype.intersect = function(ray) {return IntersectResult.noHit;};

var AreaLight = function(el, P0, P1, P2, P3) {
	this.el = el;
	this.P0 = P0;
	this.P1 = P1;
	this.P2 = P2;
	this.P3 = P3;
	this.initialize();
};

AreaLight.prototype.initialize = function() {
	this.N = (this.P0.subtract(this.P1)).cross(this.P0.subtract(this.P2)).normalize();
	//this.radius = this.P0 * this.N / (this.N * this.N);
	//this.radius = this.P0.dot(new Vector3(1 / this.N.x, 1 / this.N.y, 1 / this.N.z));
	this.radius = this.P0.dot(this.N) / (this.N.dot(this.N));
	if (this.radius < -0.00001) {
		this.N = this.N.negate();
		this.radius = -this.radius;
	}
	this.l = (this.P0.add(this.P2)).multiply(0.5);
	this.shade_x = [1 , 3 , 2 , 0 , 2 , 0 , 0 , 2 , 3 , 1 , 1 , 3 , 2 , 3 , 1 , 0];
	this.shade_y = [2 , 3 , 0 , 1 , 3 , 3 , 0 , 2 , 1 , 3 , 0 , 2 , 1 , 0 , 1 , 2];
	this.area = 0;
	this.area += Math.sqrt(this.P1.subtract(this.P0).cross(this.P2.subtract(this.P0)).sequare());
	this.area += Math.sqrt(this.P2.subtract(this.P0).cross(this.P3.subtract(this.P0)).sequare());
};

AreaLight.prototype.shade = function(scene, position) {
	var shade = 1;
	var A = this.P1.subtract(this.P0).multiply(0.25);
	var B = this.P3.subtract(this.P0).multiply(0.25);
	var rays = 16;
	for (var j = 0 ; j < rays; j++) {
		var aim = this.P0;
		aim = aim.add(A.multiply((this.shade_x[j & 15] + Math.random())));
		aim = aim.add(B.multiply((this.shade_y[j & 15] + Math.random())));
		var fra_shade = 1;
		var ray = new Ray3(position, aim.subtract(position).normalize());
		var result = scene.intersect(ray);
		if(result.geometry) {
			fra_shade = 0;
		}
		shade -= (1 - fra_shade) / rays;
	}
	return shade;
};

AreaLight.prototype.intersect = function(ray) {
	var d = this.N.dot(ray.direction);
	if(Math.abs(d) < 0.00001) {
		return IntersectResult.noHit;
	}
	var l = this.N.multiply(this.radius).subtract(ray.origin).dot(this.N) / d;
	if(l < 0.00001) {
		return IntersectResult.noHit;
	}
	var nP = ray.origin.add(ray.direction.multiply(l));
	
	var cnt = 0;
	//nP = new Vector3(0.4191062335009018, 1.5564296302380558, 0.8);
	cnt += Math.sqrt(nP.subtract(this.P0).cross(nP.subtract(this.P1)).sequare());
	cnt += Math.sqrt(nP.subtract(this.P1).cross(nP.subtract(this.P2)).sequare());
	cnt += Math.sqrt(nP.subtract(this.P2).cross(nP.subtract(this.P3)).sequare());
	cnt += Math.sqrt(nP.subtract(this.P3).cross(nP.subtract(this.P0)).sequare());
	if((cnt - this.area) > 0.0001) {
		return IntersectResult.noHit;
	}
	var result = new IntersectResult();
	result.geometry = this;
	//result.distance = nP.subtract(ray.origin).sequare();
	//result.position = nP;
	//result.normal = this.N;
	//result.tt = true;
	return result;
};

LightSample.prototype.diffuse = function(scene, position, normal, diffuse, color) {
	var shade = 1;
	var tempRay = new Ray3(position, this.l.subtract(position).normalize());
	var tempResult = scene.intersect(tempRay);
	if(tempResult.geometry) {
		shade = 0;
	}
	var L = this.l.subtract(position).normalize();
	var NdotL = normal.dot(L);
	return color.multiply(Math.max(NdotL * shade * diffuse, 0));
};

LightSample.prototype.specular = function(ray, position, normal, specular) {
	var L = this.l.subtract(position).normalize()
	var NdotL = normal.dot(L);
	var R =  L.subtract(normal.multiply(2 * NdotL));
	var NdotR = ray.direction.dot(R);
	if(NdotR > 0) {
		var secularTemp = Math.pow(NdotR, 20) * specular * shade;
		return this.el.multiply(secularTemp);
	}
	return new Color(0, 0, 0);
};

var DirectionLight = function(irradience, direction) {
	this.irradience = irradience;
	this.direction = direction;
	this.shadow = true;
};

DirectionLight.prototype.initialize = function() {
	this.l = this.direction.normalize().negate();
};

DirectionLight.prototype.sample = function(scene, position) {
	var p = position;
	if(this.shadow){
		var ray = new Ray3(position, this.l);
		var result = scene.intersect(ray);
		if(result.geometry && result.ect == -1){
			return LightSample.zero;
		}
	}
	return new LightSample(this.l, this.irradience);
};

var PointLight = function(intensity, position) {
	this.intensity = intensity;
	this.position = position;
	this.shadow = true;
};

PointLight.prototype.initialize = function() {};

PointLight.prototype.sample = function(scene, position) {
	var delta = this.position.subtract(position);
	var rr = delta.sequare();
	var r = Math.sqrt(rr);
	var l = delta.divide(r);
	if(this.shadow) {
		var ray = new Ray3(position, l);
		var result = scene.intersect(ray);
		if(result.geometry && result.distance <= r) {
			return LightSample.zero;
		}
	}
	return new LightSample(l, this.intensity.multiply(1 / rr));
};

var SpotLight = function(intensity, position, direction, theta, phi, falloff) {
	this.intensity = intensity;
	this.position = position;
	this.direction = direction;
	this.theta = theta;
	this.phi = phi;
	this.falloff = falloff;
	this.shadow = true;
};

SpotLight.prototype.initialize = function() {
	this.s = this.direction.normalize().negate();
	this.cosTheta = Math.cos(this.theta * Math.PI / 180 / 2);
	this.cosPhi = Math.cos(this.phi * Math.PI / 180 / 2);
	this.baseMultiplier = 1 / (this.cosTheta - this.cosPhi);
};

SpotLight.prototype.sample = function(scene, position) {
	var delta = this.position.subtract(position);
	var rr = delta.sequare();
	var r = Math.sqrt(rr);
	var l = delta.divide(r);

	var spot;
	var SdotL = this.s.dot(l);
	if(SdotL >= this.cosTheta) {
		spot = 1;
	} else if(SdotL <= this.cosPhi) {
		spot = 0;
	} else {
		spot = Math.pow((SdotL - this.cosPhi) * this.baseMultiplier, this.falloff);
	}

	if(this.shadow) {
		var ray = new Ray3(position, l);
		var result = scene.intersect(ray);
		if(result.geometry && result.distance <= r) {
			return LightSample.zero;
		}
	}
	return new LightSample(l, this.intensity.multiply(spot / rr));
};